package engine.hw;

import engine.config.Config;
import java.awt.Toolkit;
import math.Maths;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.PixelFormat;

public class Screen
{
    
    //<editor-fold defaultstate="collapsed" desc="Singleton">
    private static Screen singleton;
    
    public static Screen get()
    {
        if (singleton == null)
        {
            singleton = new Screen();
        }
        return singleton;
    }
    //</editor-fold>
    
    
    private static final int BITS_PER_PIXEL = 32;
    private static final int BITS_FOR_ALPHA = 8;
    private static final int BITS_FOR_STENCIL = 8;
    private static final int BITS_FOR_DEPTH = 16;
    
    
    private boolean fullscrn;
    //<editor-fold defaultstate="collapsed" desc="isFullscreen">
    public boolean isFullscreen()
    {
        return this.fullscrn;
    }
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="setFullscreen">
    public void setFullscreen(boolean fs)
    {
        this.fullscrn = fs;
    }
    //</editor-fold>
    
    private boolean vsync;
    //<editor-fold defaultstate="collapsed" desc="isVSync">
    public boolean isVSync()
    {
        return this.vsync;
    }
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="setVSync">
    public void setVSync(boolean vs)
    {
        this.vsync = vs;
    }
    //</editor-fold>
    
    private int windowW; // Specific to Windowed setting
    private int windowH;
    //<editor-fold defaultstate="collapsed" desc="getWindow[W/H]">
    public int getWindowW()
    {
        return this.windowW;
    }
    
    public int getWindowH()
    {
        return this.windowH;
    }
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="setWindow[W/H]">
    public void setWindowW(int width)
    {
        this.windowW = width;
    }
    
    public void setWindowH(int height)
    {
        this.windowH = height;
    }
    //</editor-fold>
    
    private int currentW; // Current: width, height
    private int currentH;
    //<editor-fold defaultstate="collapsed" desc="getCurrent[W/H]">
    public int getCurrentW()
    {
        return this.currentW;
    }
    
    public int getCurrentH()
    {
        return this.currentH;
    }
    //</editor-fold>
    
    private int msaaLevel;
    //<editor-fold defaultstate="collapsed" desc="getMSAALevel">
    public int getMSAALevel()
    {
        return this.msaaLevel;
    }
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="setMSAALevel">
    public void setMSAALevel(int msaa)
    {
        this.msaaLevel = Maths.capi(msaa, 0, 32);
    }
    //</editor-fold>
    
    private float aspectRatio;
    //<editor-fold defaultstate="collapsed" desc="getAspectRatio">
    public float getAspectRatio()
    {
        return this.aspectRatio;
    }
    //</editor-fold>
    
    private int maxDesiredFPS;
    //<editor-fold defaultstate="collapsed" desc="setMaxDesiredFPS">
    public void setMaxDesiredFPS(int desiredFPS)
    {
        this.maxDesiredFPS = desiredFPS;
    }
    //</editor-fold>
    
    private DisplayMode displayMode;
    private PixelFormat pixelFormat;
    
    private Screen() 
    {
    }

    public boolean isCloseRequested() 
    {
        return Display.isCloseRequested();
    }
    
    public void setTitle(String title)
    {
        Display.setTitle(title);
    }
    
    //<editor-fold defaultstate="collapsed" desc="getScreen[W/H]">
    public int getScreenW()
    {
        return Toolkit.getDefaultToolkit().getScreenSize().width;
    }
    
    public int getScreenH()
    {
        return Toolkit.getDefaultToolkit().getScreenSize().height;
    }
    //</editor-fold>
    
    public void initialize(Config cfgFile) // int width, int height, boolean fs, boolean vs, int aa
    {
        System.setProperty("org.lwjgl.util.NoChecks", "true");
        System.setProperty("org.lwjgl.util.Debug", "false");
        this.setTitle(cfgFile.getStr("WindowTitle"));
        this.setWindowW(cfgFile.getInt("WindowedWidth"));
        this.setWindowH(cfgFile.getInt("WindowedHeight"));
        this.setFullscreen(cfgFile.getBool("Fullscreen"));
        this.setVSync(cfgFile.getBool("VSync"));
        this.setMSAALevel(cfgFile.getInt("MSAALevel"));
        this.initCurrentDimensions();
        this.aspectRatio = this.getAspectRatio(this.currentW, this.currentH);
        this.displayMode = this.getDisplayMode(
            this.currentW, 
            this.currentH, 
            Screen.BITS_PER_PIXEL, 
            this.fullscrn
        );
        this.pixelFormat = this.getPixelFormat(
            Screen.BITS_PER_PIXEL, // bpp
            Screen.BITS_FOR_ALPHA, // alphaBits
            Screen.BITS_FOR_STENCIL, // stencilBits
            Screen.BITS_FOR_DEPTH, // depthBits
            this.msaaLevel // MSAA samples
        );
        try 
        {
            Display.setVSyncEnabled(this.vsync);
            Display.setDisplayModeAndFullscreen(this.displayMode);
            Display.create(this.pixelFormat);
            Display.setDisplayConfiguration(
                1.0f, // Gamma (default: 1.0)
                0.0f, // Brightness (default: 0.0)
                1.0f  // Contrast (default: 1.0)
            );
        }
        catch (LWJGLException e)
        {
            System.out.println(e);
            System.exit(1);
        }
    }

    public void update() 
    {
        Display.update();
        Display.sync(this.maxDesiredFPS);
    }

    public void destroy() 
    {
        Display.destroy();
    }
    
    
    private void initCurrentDimensions()
    {
        if (this.fullscrn)
        {
            this.currentW = this.getScreenW();
            this.currentH = this.getScreenH();
        }
        else
        {
            this.currentW = this.windowW;
            this.currentH = this.windowH;
        }
    }
    
    private float getAspectRatio(int width, int height)
    {
        return ((float) width) / height;
    }
    
    private DisplayMode getDisplayMode(int width, int height, int bpp, boolean fs)
    {
        DisplayMode[] displayModes = null;
        try
        {
            displayModes = Display.getAvailableDisplayModes();
        }
        catch (LWJGLException e)
        {
            System.out.println(e);
            System.exit(1);
        }
        DisplayMode testMode;
        for (int i = 0; i < displayModes.length; i++)
        {
            testMode = displayModes[i];
            boolean matchW = (width == testMode.getWidth());
            boolean matchH = (height == testMode.getHeight());
            boolean matchBPP = (bpp == testMode.getBitsPerPixel());
            boolean matchFS = (testMode.isFullscreenCapable() || !fs);
            if (matchW && matchH && matchBPP && matchFS)
            {
                return testMode;
            }
        }
        return null;
    }
    
    private PixelFormat getPixelFormat(
            int bpp, 
            int alphaBits, 
            int stencilBits, 
            int depthBits, 
            int msaaSamples)
    {
        PixelFormat pixFormat = new PixelFormat()
            .withBitsPerPixel(bpp)
            .withAlphaBits(alphaBits)
            .withStencilBits(stencilBits)
            .withDepthBits(depthBits)
            .withSamples(msaaSamples)
            .withSRGB(true)
        ;
        return pixFormat;
    }
    
    private void printPixelFormat(PixelFormat pixFormat)
    {
        System.out.println("BPP:          " + pixFormat.getBitsPerPixel());
        System.out.println("alphaBits:    " + pixFormat.getAlphaBits());
        System.out.println("stencilBits:  " + pixFormat.getStencilBits());
        System.out.println("depthBits:    " + pixFormat.getDepthBits());
        System.out.println("MSAA Samples: " + pixFormat.getSamples());
        System.out.println("is SRGB:      " + pixFormat.isSRGB());
        System.out.println("is Stereo:    " + pixFormat.isStereo());
        System.out.println("is FloatPt:   " + pixFormat.isFloatingPoint());
        System.out.println("accumAlpha:   " + pixFormat.getAccumulationAlpha());
        System.out.println("accumBPP:     " + pixFormat.getAccumulationBitsPerPixel());
        System.out.println("auxBuffers:   " + pixFormat.getAuxBuffers());
    }
    
}
